/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package zombiedefense.sprites;

import java.awt.Color;
import zombiedefense.util.MathEx;
import zombiedefense.util.Timer;
import java.awt.Graphics2D;
import zombiedefense.map.Path;
import java.awt.Image;
import java.awt.geom.Point2D;
import zombiedefense.map.Raycaster;
import zombiedefense.map.TileMap;
import static zombiedefense.util.MathEx.loadImageFromJar;

/**
 *
 * @author Willisons
 */
public class Zombie extends Sprite {

    private Path path;
    private Point2D.Double nextPathPoint = new Point2D.Double();
    private static final double pathTolerance = .2;
    private static final double pathToleranceSquared = pathTolerance * pathTolerance;
    private static final double attackRange = .5;
    private static final double attackRangeSquared = attackRange * attackRange;
    private static final long minPathDelay = 3000;
    private static final long maxPathDelay = 5000;
    private static final int zombieZ = 1;
    private final long pathUpdateDelay = MathEx.getRandomLong(minPathDelay, maxPathDelay);
    private Timer pathTimer = new Timer(pathUpdateDelay); //to measure time between path updates (A*)
    private boolean playerInSight = false;
    private boolean isAttacking = false;
    private ZombieType type;

    private Zombie(double x, double y, ZombieType t) {
	super(getAnims(), t.walking, x, y, zombieZ, t.health);

	this.type = t;
	pathTimer.start();
	//update path
	updatePath();
	followPath();
    }

    public void update(long elapsedTime) {

	//update timer
	pathTimer.update(elapsedTime);

	//do not try to follow player if attacking
	if(!isAttacking){
            
	    //logic to go towards player if seen
	    if (rayCaster.raycast(
		    this,
		    null,
		    centerX(),
		    centerY(),
		    pathFinder.getPlayer().centerX(),
		    pathFinder.getPlayer().centerY(),
		    (byte) (Raycaster.DETECT_PLAYER_MASK | Raycaster.DETECT_WALLS_MASK)) == Raycaster.DETECT_PLAYER_MASK) {

		//can see player
		playerInSight = true;

		//turn to face player and travel to him
		setGoal(type.speed, pathFinder.getPlayer().centerX(),
			pathFinder.getPlayer().centerY());

	    } else {
		//just lost player, must recalc path
		if (playerInSight) {
		    playerInSight = false;
		    updatePath();
		}else{

		//update path timer
		if (pathTimer.hasTicked()) {

		    //pathfind to player again
		    updatePath();

		    //reset timer
		    pathTimer.reset();
		} else {
		    //use path object to find player
		    followPath();
		}
		}
	    }
	}else{
	    //if attacking, stop moving
	    velocity.x = 0;
            velocity.y = 0;
	}

	//change animation
	if (velocity.x != 0 || velocity.y != 0) {
	    setAnimation(anims[type.walking]);
	}else if(isAttacking){
	    setAnimation(anims[type.attacking]);
	}else{
	    setAnimation(anims[type.standing]);
	}

	//move zombie
	super.update(elapsedTime);
    }

    public void draw(Graphics2D g, int drawx, int drawy, TileMap map) {
	super.draw(g, drawx, drawy, map);

//	//draw the path for testing
//	if (path != null) {
//	    path.draw(g, drawx, drawy, map, Color.red);
//	}
    }
    
    public void checkIsPlayerInRange(){
	Player p = pathFinder.getPlayer();
	if(MathEx.getDistanceSquared(centerX(),centerY(),p.centerX(),p.centerY()) <= attackRangeSquared){
	    collision(p);
	    p.collision(this);
	    
	    isAttacking = true;
	}else{
	    isAttacking = false;
	}
    }

    /**
     * Sets the zombie moving to the next location on
     * the current Path.
     */
    private void followPath() {

	if (path == null) {
	    //stop the sprite
	    velocity.x = 0;
            velocity.y = 0;
	    return;
	}

	//advance to next path point if at the current point
	if (isAtNextPoint()) {
	    advanceGoalPoint();
	}

	//begin traveling to the next point
	setGoal(type.speed, nextPathPoint.x, nextPathPoint.y);
    }

    public ZombieType getType(){
	return type;
    }
    
    private void advanceGoalPoint() {
	Point2D.Double p = path.getNextCoords();
	nextPathPoint.setLocation(p.x, p.y);
    }

    private void updatePath() {
	//get new path
	path = pathFinder.getPath(centerX(),
		centerY(),
		pathFinder.getPlayer().centerX(),
		pathFinder.getPlayer().centerY());

	//start following the path
	advanceGoalPoint();
	followPath();

    }
//    for testing - draw path even though zombie is offscreen
//    public boolean isOffScreen(int swidth, int sheight, int drawx, int drawy) {
//	return false;
//    }

    /**
     * Uses pythagorean theorem to find out if zombie is close to path point.
     * @return 
     */
    private boolean isAtNextPoint() {
	return MathEx.getDistanceSquared(centerX(),
		centerY(),
		nextPathPoint.x,
		nextPathPoint.y) <= pathToleranceSquared;
    }

    public void collision(Sprite other) {
    
    }

    public void rayHit(Sprite source) {
	//zombie was hit with a bullet
	if(source instanceof Player){
	    Player p = (Player)source;
	    hit(p.getGun().getDamage());
	    
	    if(health <= 0){
		p.notifyKilled(this);
	    }
	}
    }

    /**
     * Factory method for simplifying zombie creation.
     * @param x
     * @param y
     * @return 
     */
    public static Zombie createZombie(ZombieType t, double x, double y) {
	return new Zombie(x, y, t);
    }
    
    public enum ZombieType {

	basicZombie(ZombieType.basicStanding, ZombieType.basicWalking, ZombieType.basicAttacking, 20, .5, .002);
	public static final int basicStanding = 0;
	public static final int basicWalking = 1;
	public static final int basicAttacking = 2;
	private int standing, walking, attacking;
	private int health;
	private double speed,power;

	ZombieType(int standing, int walking, int attacking, int health, double power, double speed) {
	    this.standing = standing;
	    this.walking = walking;
	    this.attacking = attacking;
	    this.health = health;
	    this.power = power;
	    this.speed = speed;
	}

	public int getHealth() {
	    return health;
	}

	public double getPower() {
	    return power;
	}

	public double getSpeed() {
	    return speed;
	}
	
	
    };
    
    protected static Animation[] getAnims() {
	Animation[] anims = {
	    new Animation(),
	    new Animation(),
	    new Animation()
	};

	Image m = loadImageFromJar("/images/basiczombie1.png");
	
	anims[ZombieType.basicStanding].addFrame(m, 0);
	anims[ZombieType.basicStanding].setLoop(false);

	anims[ZombieType.basicWalking].addFrame(loadImageFromJar("/images/basiczombie2.png"), 400);
	anims[ZombieType.basicWalking].addFrame(loadImageFromJar("/images/basiczombie3.png"), 400);

	
	Image i = loadImageFromJar("/images/basiczombie4.png");

	anims[ZombieType.basicAttacking].addFrame(m, 100);
	anims[ZombieType.basicAttacking].addFrame(i, 100);
	anims[ZombieType.basicAttacking].addFrame(loadImageFromJar("/images/basiczombie5.png"), 100);
	anims[ZombieType.basicAttacking].addFrame(i, 100);

	return anims;
    }
}
